// Last Update:2015-01-23 15:49:08
/**
 * @file pcap_lib.c
 * @brief 
 * @author zealoussnow@163.com
 * @version 0.1.00
 * @date 2014-12-25
 */

#include <stdio.h>
#include <stdlib.h>
#include <pcap.h>
#include <time.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/in.h>

#include <unistd.h>

#define GET  0x20544547
#define POST 0x54534f50

int dump_netinfo(char *dev)
{
	int ret;
	struct in_addr addr;
    char errbuf[PCAP_ERRBUF_SIZE] = {0};
    char *net;  /* dot notation of the network address */
    char *mask; /* dot notation of the network mask */
    bpf_u_int32 netp; /* ip */
    bpf_u_int32 maskp; /* subnet mask */

	/* Find the ipv4 network number and mask */
    ret = pcap_lookupnet(dev, &netp, &maskp, errbuf);
    if (ret == -1)
    {
        fprintf(stderr, "%s\n", errbuf);
		return -1;
    }
    addr.s_addr = netp;
    net = inet_ntoa(addr);
    if (net == NULL)
    {
        perror("inet_ntoa");
		return -1;
    }
    printf("NET: %s\n", net);

    addr.s_addr = maskp;
    mask = inet_ntoa(addr);
    if (mask == NULL)
    {
        perror("inet_ntoa");
        exit(EXIT_FAILURE);
    }
    printf("MASK: %s\n", mask);

	return 0;
}

void parse_http_get(const u_char *packet, struct ether_header *eth_hdr,
		struct iphdr *ip_hdr, struct tcphdr *tcp_hdr, void *payload, int tcplen)
{
	char *data = (char *)payload;
	printf("%s\n", data);
}

void parse_tcp(const u_char *packet, struct ether_header *eth_hdr, struct iphdr *ip_hdr, struct tcphdr *tcp_hdr)
{
	int iphdr_len = (ip_hdr->ihl & 0x0f) << 2;
	int tcphdr_len = (tcp_hdr->doff & 0x0f) << 2;
	int tcplen = ntohs(ip_hdr->tot_len) - iphdr_len - tcphdr_len;
	void *payload = (void *)((uint8_t *)tcp_hdr + tcphdr_len);

	if (tcplen > 1516 - iphdr_len - tcphdr_len)
	{
		tcplen = 1516 - iphdr_len - tcphdr_len;
	}
	if (tcplen > 0)
	{
		if (*(uint32_t *)payload == GET)
		{
			parse_http_get(packet, eth_hdr, ip_hdr, tcp_hdr, payload, tcplen);
		}
		else if (*(uint32_t *)payload == POST)
		{
			// TODO 
			//parse_http_post(packet, eth_hdr, ip_hdr, tcp_hdr, payload, tcplen);
		}
	}
}

void parse_ipv4(const u_char *packet, struct ether_header *eth_hdr, struct iphdr *ip_hdr)
{
	int iphdr_length = (ip_hdr->ihl & 0x0f) << 2;

	switch (ip_hdr->protocol)
	{
		case IPPROTO_TCP:
			{
				struct tcphdr *tcp_hdr = (struct tcpdhr *)((uint8_t*)ip_hdr + iphdr_length);
				parse_tcp(packet, eth_hdr, ip_hdr, tcp_hdr);
			}
			break;
		case IPPROTO_UDP:
			{
				// TODO
				// parse_udp();
			}
			break;
		default:
			break;
	}
}

void process_packet(const u_char *packet, struct pcap_pkthdr hdr)
{
    printf("Grabbed packet of length %d\n", hdr.len);
    printf("Received at... %s\n", ctime((const time_t *)&hdr.ts.tv_sec));
    printf("Ethernet address length is: %d\n", ETHER_HDR_LEN);

    struct ether_header *eptr = (struct ether_header *)packet;
    if (ntohs(eptr->ether_type) == ETHERTYPE_IP)
    {
        printf("Ethernet type hex: %x, dec: %d is an ip packet\n", 
                ntohs(eptr->ether_type), ntohs(eptr->ether_type));
		struct iphdr *ipv4_hdr = (struct iphdr *)((uint16_t *)(eptr + 1) + 2);
		parse_ipv4(packet, eptr, ipv4_hdr);
    }
	else if (ntohs(eptr->ether_type) == ETHERTYPE_ARP)
	{
		printf("Ether type hex: %x, dec: %d is an  ARP packet\n",
				ntohs(eptr->ether_type), ntohs(eptr->ether_type));
	}
	else if (ntohs(eptr->ether_type) == ETHERTYPE_IPV6)
	{
		printf("Ether type hex: %x, dec: %d is an IPV6 packet\n",
				ntohs(eptr->ether_type), ntohs(eptr->ether_type));
	}
    else
    {
        printf("Ethernet type %x not IP\n", ntohs(eptr->ether_type));
        exit(EXIT_FAILURE);
    }

    u_char *ptr = NULL;
    ptr = eptr->ether_dhost;
    int i = ETHER_ADDR_LEN;
    printf("Destination Address: \n");
    do {
        printf("%s %02x", (i == ETHER_ADDR_LEN) ? " " : ":", *ptr++);
    } while (--i > 0);
    printf("\n");

    ptr = eptr->ether_shost;
    i = ETHER_ADDR_LEN;
    printf("Source Address: \n");
    do {
        printf("%s %02x", (i == ETHER_ADDR_LEN) ? " " : ":", *ptr++);
    } while (--i > 0);
    printf("\n");
}

int get_packet(pcap_t *pcap_descr, const u_char **packet, struct pcap_pkthdr *hdr)
{
    *packet = pcap_next(pcap_descr, hdr);
    if (*packet == NULL)
    {
        return 0;
    }

	return 1;
}

char *dev_init(int argc, char *argv[])
{
	char *dev = NULL;	/* Name of the device to use */

    /* Ask pcap to find a valid device for use to sniff on */
#if defined AUTO_DETECT_DEV
    char errbuf[PCAP_ERRBUF_SIZE] = {0};
    dev = pcap_lookupdev(errbuf);
    if (dev == NULL)
    {
        fprintf(stderr, "%s\n", errbuf);
        exit(EXIT_FAILURE);
    }
#else
	if (argc < 2)
	{
		fprintf(stderr, "Usage: %s [NIC|pcap_file]\n", argv[0]);
		exit(EXIT_FAILURE);
	}
	dev = argv[1];
#endif

	return dev;
}

int main(int argc, char *argv[])
{
    char errbuf[PCAP_ERRBUF_SIZE] = {0};
	char *dev = dev_init(argc, argv);

	printf("DEV: %s\n", dev);

#if defined OPEN_LIVE
	if ( (dump_netinfo(dev)) == -1)
	{
		fprintf(stderr, "dump network info error\n");
		exit(EXIT_FAILURE);
	}

	/* Open NIC and capture packets */
    pcap_t *descr = pcap_open_live(dev, BUFSIZ, 0, -1, errbuf);
    if (descr == NULL)
    {
        fprintf(stderr, "pcap_open_live: %s\n", errbuf);
        exit(EXIT_FAILURE);
    }
#else
	pcap_t *descr = pcap_open_offline(dev, errbuf);
	if (descr == NULL)
	{
		fprintf(stderr, "pcap_open_offline: %s\n", errbuf);
		exit(EXIT_FAILURE);
	}
#endif
	
    struct pcap_pkthdr hdr; /* pcap.h */
	const u_char *packet = NULL;
	while (1)
	{
		if (get_packet(descr, &packet, &hdr) > 0)
		{
			printf("before: %p\n", packet);
			process_packet(packet, hdr);
		}

		/* If no packet arrive, what do we want to do? */
	}

    pcap_close(descr);

    return 0; 
}
